package com.quectel.cms.controller.sys;


import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.quectel.business.common.util.sys.SmallFileUtils;
import com.quectel.cms.controller.BaseController;
import com.quectel.cms.util.SessionHolder;
import com.quectel.constant.global.SystemConstants;
import com.quectel.constant.global.UploadConstants;
import com.quectel.sys.module.sys.dto.SmallFileDto;
import com.quectel.sys.module.sys.service.SmallFileService;
import com.quectel.util.common.ImageUtils;
import com.quectel.util.common.Response;
import com.quectel.util.common.ServletUtils;
import com.quectel.util.redis.RedisUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.Lock;

/**
 * @author caolu
 * @email louis.cao@quectel.com
 * @date 2022/09/20 13:11
 */
@RestController
@RequestMapping("smallFile")
@Tag(name = "SmallFileController", description = "小文件存储相关api")
public class SmallFileController extends BaseController {

    @DubboReference
    private SmallFileService smallFileService;

    private static final String UPLOAD_LOCK = "UPLOAD_LOCK";

    /**
     * 小文件上传
     *
     * @param request
     * @return
     */
    @PostMapping("upFile")
    @Operation(summary = "小文件上传")
    public Response<Object> upFile(MultipartHttpServletRequest request) {
        Iterator<String> fileNames = request.getFileNames();
        while (fileNames.hasNext()) {
            String next = fileNames.next();
            List<MultipartFile> files = request.getFiles(next);
            for (MultipartFile file : files) {
                int i = 0;
                for (i = 0; i < UploadConstants.FILE_ACCEPT_TYPE.length; i++) {
                    if (file.getOriginalFilename().endsWith(UploadConstants.FILE_ACCEPT_TYPE[i])) {
                        break;
                    }
                }
                if (i >= UploadConstants.FILE_ACCEPT_TYPE.length) {
                    return Response.error("不支持的文件格式");
                }
                if (file.getSize() > UploadConstants.SMALL_FILE_BYTES_LIMIT) {
                    return Response.error("文件不能大于1M");
                }
                File tempFile = null;
                try {
                    tempFile = ImageUtils.compressImgWriteToTempDir(file.getInputStream(), file.getOriginalFilename(), false);

                    String type = FileUtil.getType(tempFile);
                    if (type == null) {
                        return Response.error("无法获取文件扩展名，请重试");
                    }

                    String fileSha1Hex = DigestUtil.sha1Hex(tempFile);
                    Lock spinLock = RedisUtils.getSpinLock(UPLOAD_LOCK + SessionHolder.getSysUser().getTenantId() + ":" + fileSha1Hex);
                    spinLock.lock();
                    try {
                        SmallFileDto smallFileDto = smallFileService.selectBySha1Code(SessionHolder.getSysUser().getTenantId(), fileSha1Hex);
                        if (smallFileDto == null) {
                            smallFileDto = new SmallFileDto();
                            smallFileDto.setTenantId(SessionHolder.getSysUser().getTenantId());
                            smallFileDto.setFileName(file.getOriginalFilename());
                            smallFileDto.setFileType(type);
                            smallFileDto.setSha1Code(fileSha1Hex);

                            smallFileDto.setOpenTenantValidate(
                                    Boolean.TRUE.toString().equalsIgnoreCase(request.getParameter("onlyTenantAccess")) ?
                                            SystemConstants.YES : SystemConstants.NO
                            );

                            smallFileDto.setFileBase64Data(
                                    Base64.getEncoder().encodeToString(
                                            FileUtil.readBytes(tempFile)
                                    )
                            );
                            smallFileDto.setFileSize(file.getSize());
                            smallFileDto.setRemark("由管理员:" + SessionHolder.getSysUser().getAccount() + "上传");
                            smallFileDto.setCreateTime(new Date());
                            Long id = smallFileService.save(smallFileDto);
                            smallFileDto.setId(id);
                        }
                        return Response.ok().wrap(String.valueOf(smallFileDto.getId()));
                    } catch (Exception e) {
                        logger.error("Save small file error");
                    } finally {
                        spinLock.unlock();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    FileUtil.del(tempFile);
                }
            }
        }
        return Response.error("上传失败");
    }

    /**
     * 小文件上传
     *
     * @param request
     * @return
     */
    @PostMapping("upImg")
    @Operation(summary = "小图片上传")
    public Response<Object> upImg(MultipartHttpServletRequest request) {
        Iterator<String> fileNames = request.getFileNames();
        while (fileNames.hasNext()) {
            String next = fileNames.next();
            List<MultipartFile> files = request.getFiles(next);
            for (MultipartFile file : files) {
                int i = 0;
                for (i = 0; i < UploadConstants.IMAGE_TYPES.length; i++) {
                    if (file.getOriginalFilename().endsWith(UploadConstants.IMAGE_TYPES[i])) {
                        break;
                    }
                }
                if (i >= UploadConstants.IMAGE_TYPES.length) {
                    return Response.error("不支持的图片格式");
                }
                if (file.getSize() > UploadConstants.SMALL_FILE_BYTES_LIMIT) {
                    return Response.error("文件不能大于1M");
                }
                File tempFile = null;
                try {
                    tempFile = ImageUtils.compressImgWriteToTempDir(file.getInputStream(), file.getOriginalFilename(), false);

                    String type = FileUtil.getType(tempFile);
                    if (type == null) {
                        return Response.error("无法获取文件扩展名，请重试");
                    }

                    String fileSha1Hex = DigestUtil.sha1Hex(tempFile);
                    Lock spinLock = RedisUtils.getSpinLock(UPLOAD_LOCK + SessionHolder.getSysUser().getTenantId() + ":" + fileSha1Hex);
                    spinLock.lock();
                    try {
                        SmallFileDto smallFileDto = smallFileService.selectBySha1Code(SessionHolder.getSysUser().getTenantId(), fileSha1Hex);
                        if (smallFileDto == null) {
                            smallFileDto = new SmallFileDto();
                            smallFileDto.setTenantId(SessionHolder.getSysUser().getTenantId());
                            smallFileDto.setFileName(file.getOriginalFilename());
                            smallFileDto.setFileType(type);
                            smallFileDto.setSha1Code(fileSha1Hex);

                            smallFileDto.setOpenTenantValidate(
                                    Boolean.TRUE.toString().equalsIgnoreCase(request.getParameter("onlyTenantAccess")) ?
                                            SystemConstants.YES : SystemConstants.NO
                            );

                            smallFileDto.setFileBase64Data(
                                    Base64.getEncoder().encodeToString(
                                            FileUtil.readBytes(tempFile)
                                    )
                            );
                            smallFileDto.setFileSize(file.getSize());
                            smallFileDto.setRemark("由管理员:" + SessionHolder.getSysUser().getAccount() + "上传");
                            smallFileDto.setCreateTime(new Date());
                            Long id = smallFileService.save(smallFileDto);
                            smallFileDto.setId(id);
                        }
                        return Response.ok().wrap(String.valueOf(smallFileDto.getId()));
                    } catch (Exception e) {
                        logger.error("Save small file error", e);
                    } finally {
                        spinLock.unlock();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    FileUtil.del(tempFile);
                }
            }
        }
        return Response.error("上传失败");
    }

    /**
     * 小文件下载或者获取
     *
     * @param fileId 文件的id
     * @return
     */
    @GetMapping("download")
    @Operation(summary = "小文件下载")
    public void downloadOrShow(@Parameter(description = "文件上传的id") @RequestParam Long fileId, HttpServletResponse response) {
        SmallFileDto smallFileDto = SmallFileUtils.getByFileId(fileId);
        if (smallFileDto == null) {
            try {
                response.getOutputStream().write("File not exists".getBytes(StandardCharsets.UTF_8));
            } catch (IOException e) {
                logger.error("Write file not exists message error");
            }
            return;
        }
        if (SystemConstants.YES.equals(smallFileDto.getOpenTenantValidate())) {
            //开启租户级校验
            if (SessionHolder.getSysUser() == null || !SessionHolder.getSysUser().getTenantId().equals(smallFileDto.getTenantId())) {
                //访问人所属租户和文件租户不一致
                try {
                    response.getOutputStream().write("File not exists".getBytes(StandardCharsets.UTF_8));
                } catch (IOException e) {
                    logger.error("Write file not exists message error");
                }
                return;
            }
        }

        ServletUtils.write(
                response,
                new ByteArrayInputStream(Base64.getDecoder().decode(smallFileDto.getFileBase64Data())),
                smallFileDto.getFileName()
        );
    }

    /**
     * 图片显示
     *
     * @param fileId 文件的id
     * @return
     */
    @GetMapping("imgShow")
    @Operation(summary = "小图片显示")
    public void imgShow(@Parameter(description = "文件上传的id") @RequestParam Long fileId, HttpServletResponse response) {
        SmallFileDto smallFileDto = SmallFileUtils.getByFileId(fileId);
        if (smallFileDto == null) {
            try {
                response.getOutputStream().write("File not exists".getBytes(StandardCharsets.UTF_8));
            } catch (IOException e) {
                logger.error("Write file not exists message error");
            }
            return;
        }
        if (SystemConstants.YES.equals(smallFileDto.getOpenTenantValidate())) {
            //开启租户级校验
            if (SessionHolder.getSysUser() == null || !SessionHolder.getSysUser().getTenantId().equals(smallFileDto.getTenantId())) {
                //访问人所属租户和文件租户不一致
                try {
                    response.getOutputStream().write("File not exists".getBytes(StandardCharsets.UTF_8));
                } catch (IOException e) {
                    logger.error("Write file not exists message error");
                }
                return;
            }
        }
        ServletUtils.writeImg(
                response,
                new ByteArrayInputStream(Base64.getDecoder().decode(smallFileDto.getFileBase64Data())),
                smallFileDto.getFileName(),
                smallFileDto.getFileType()
        );
    }
}
